{
  lib,
  buildPackages,
  callPackage,
  stdenv,
  nim1,
  nim2,
  nim_builder,
  defaultNimVersion ? 2,
  nimOverrides,
  buildNimPackage,
}:

let
  baseAttrs = {
    strictDeps = true;
    enableParallelBuilding = true;
    doCheck = true;
    configurePhase = ''
      runHook preConfigure
      export NIX_NIM_BUILD_INPUTS=''${pkgsHostTarget[@]} $NIX_NIM_BUILD_INPUTS
      nim_builder --phase:configure
      runHook postConfigure
    '';
    buildPhase = ''
      runHook preBuild
      nim_builder --phase:build
      runHook postBuild
    '';
    checkPhase = ''
      runHook preCheck
      nim_builder --phase:check
      runHook postCheck
    '';
    installPhase = ''
      runHook preInstall
      nim_builder --phase:install
      runHook postInstall
    '';
    meta = { inherit (nim2.meta) maintainers platforms; };
  };

  fodFromLockEntry =
    let
      methods = {
        fetchzip =
          { url, sha256, ... }:
          buildPackages.fetchzip {
            name = "source";
            inherit url sha256;
          };
        git =
          {
            fetchSubmodules,
            leaveDotGit,
            rev,
            sha256,
            url,
            ...
          }:
          buildPackages.fetchgit {
            inherit
              fetchSubmodules
              leaveDotGit
              rev
              sha256
              url
              ;
          };
      };
    in
    attrs@{ method, ... }:
    let
      fod = methods.${method} attrs;
    in
    ''--path:"${fod.outPath}/${attrs.srcDir}"'';

  asFunc = x: if builtins.isFunction x then x else (_: x);

in
buildNimPackageArgs:
let
  composition =
    finalAttrs:
    let
      postPkg = baseAttrs // (asFunc ((asFunc buildNimPackageArgs) finalAttrs)) baseAttrs;

      lockAttrs = lib.attrsets.optionalAttrs (builtins.hasAttr "lockFile" postPkg) (
        builtins.fromJSON (builtins.readFile postPkg.lockFile)
      );

      lockDepends = lockAttrs.depends or [ ];

      lockFileNimFlags = map fodFromLockEntry lockDepends;

      postNimOverrides = builtins.foldl' (
        prevAttrs:
        { packages, ... }@lockAttrs:
        builtins.foldl' (
          prevAttrs: name:
          if (builtins.hasAttr name nimOverrides) then
            (prevAttrs // (nimOverrides.${name} lockAttrs prevAttrs))
          else
            prevAttrs
        ) prevAttrs packages
      ) postPkg lockDepends;

      finalOverride =
        {
          depsBuildBuild ? [ ],
          nativeBuildInputs ? [ ],
          nimFlags ? [ ],
          requiredNimVersion ? defaultNimVersion,
          passthru ? { },
          ...
        }:
        (
          if requiredNimVersion == 1 then
            {
              depsBuildBuild = [ nim_builder ] ++ depsBuildBuild;
              nativeBuildInputs = [ nim1 ] ++ nativeBuildInputs;
            }
          else if requiredNimVersion == 2 then
            {
              depsBuildBuild = [ nim_builder ] ++ depsBuildBuild;
              nativeBuildInputs = [ nim2 ] ++ nativeBuildInputs;
            }
          else
            throw "requiredNimVersion ${toString requiredNimVersion} is not valid"
        )
        // {
          nimFlags = lockFileNimFlags ++ nimFlags;
          passthru = passthru // {
            # allow overriding the result of buildNimPackageArgs before this composition is applied
            # this allows overriding the lockFile for packages built using buildNimPackage
            # this is adapted from mkDerivationExtensible in stdenv.mkDerivation
            overrideNimAttrs =
              f0:
              let
                f =
                  self: super:
                  let
                    x = f0 super;
                  in
                  if builtins.isFunction x then f0 self super else x;
              in
              buildNimPackage (
                self:
                let
                  super = (asFunc ((asFunc buildNimPackageArgs) self)) baseAttrs;
                in
                super // (if builtins.isFunction f0 || f0 ? __functor then f self super else f0)
              );
          };
        };

      attrs = postNimOverrides // finalOverride postNimOverrides;
    in
    lib.trivial.warnIf (builtins.hasAttr "nimBinOnly" attrs)
      "the nimBinOnly attribute is deprecated for buildNimPackage"
      attrs;

in
stdenv.mkDerivation composition
